\section{Analyse sémantique}

L'analyse sémantique se fait sur l'arbre syntaxique en deux étapes.
Une première étape servant à déterminer le type des variables et une
deuxième étape qui va générer le code llvm.


\subsection{Typage}

L'analyse du type se fait de façon récursive. On appelle parcoursType
sur la racine de l'arbre qui appelle alors récursivement parcoursType
sur ces fils. Quand on arrive à une feuille on peut alors déterminer
le type. Dans le cas où le noeud est l'identifiant d'une variable, on
regarde si il a déjà été ajouté à la pile des variables si non on
l'ajoute puis on copie son type dans le noeud. Ensuite, on remonte
dans l'arbre et chaque type de noeud va avoir ces règles pour vérifier
que le typage est bon.


Lorsque l'on affecte une valeur à un identifiant, le type de
l'identifiant devient le type de la valeur.  Si une nouvelle
affectation est réalisée sur le même identifiant avec un type
différent, la compilation est arrêtée et un message d'erreur s'affiche.
Il en est de même si une affectation est faite à partir d'un autre
identifiant d'un type diffèrent ou de type inconnu jusqu'à présent.
Si on essaye d'exécuter une opération ``interdite'' par exemple une
addition entre un entier et une chaîne de caractères.

Nous stockons dans une pile les variables, afin de pouvoir restreindre
la portée de celles-ci.  Lorsque nous entrons dans un bloc nous
empilons un élément de début de bloc et lorsque nous sortons du bloc
nous dépilons toutes les variables jusqu'à l'élément de début. Ainsi
toutes les variable apparaissant pour la première fois dans un bloc ne
sont plus accessible après ce bloc. Par exemple, toute variable
accessible avant un bloc ``if'' sont accessibles dans le bloc et après
sans pouvoir changer son type.  Un variable utilisée pour la première
fois dans le if sera inaccessible en dehors.  

Une pile de variables globales est aussi créée (bien sur on ne dépile
jamais).

Pour la définition de fonction, nous générons tous les corps de
fonction possibles pour chaque combinaisons de types possibles pour
les arguments.  Nous balayons le code de la fonction en cherchant
d'éventuelles erreurs de typages. S'il n'y a aucune erreur, le code
sera généré et la fonction ajoutée à la pile des fonctions.

\subsection{Génération du code}

La génération de code se fait aussi par une fonction récursive. Cette
fois quand on arrive aux feuilles on génère la ligne de code
correspondante. Et lors de la remonté dans l'arbre on concatène les
codes des fils et celui correspondant au noeud actuel.  Pour les
définitions de fonctions le code est généré pendant l'analyse de type
après avoir trouver une combinaison de type correcte pour les
paramètres. Le code généré est directement ajouté à une variable
globale, cela permet de mettre toute les définitions de fonctions
avant le ``main'' dans le fichier.

L'instruction alloca est appelée lorsqu'on affecte une valeur à un
identifiant. Nous allouons un registre qui commencera par \%u suivi
d'un numéro qui s'incrémentera au fur et à mesure des allocations afin
d'avoir un numéro de registre unique. Nous mettons la valeur à
affecter dans un registre temporaire commençant par \%r puis un nombre
de la même manière que précédemment. Nous faisons ensuite un store
pour affecter la valeur.

Nous faisons un load lorsqu'un identifiant est ``utilisé'', par
exemple pour a = b; nous chargeons b dans un registre temporaire avant
de l'affecter à a.

Nous utilisons des doubles en llvm pour représenter les nombres
flottants car nous avions un problème pour la représentation des
nombres comme par exemple 1.3 qui n'a pas de représentation fini en
binaire sur un float. Le problème disparaît lorsque l'on utilise un
double.

\subsection{Limites}

Les fonctions récursives ne sont pas supportées. Lors de l'analyse de
type dans la création d'une fonction récursive, l'appelle récursif
serait vue comme un appelle d'une fonction inconnue car à ce moment là
la fonction n'est pas encore dans la pile des fonctions. De plus, si
le type de retour dépend d'un appelle récursif de la fonction cela aboutirait la
plupart du temps à un type de retour inconnu. La fonction ne serait
alors pas compilée.

Les définitions de fonctions ne retournant rien génère du code
incorrecte: en llvm toutes les fonctions doivent finir par une
instruction ret. Ainsi, les fonctions rubic qui ne se termine pas par
un return génère elle aussi un code incorrecte.

Remarque: la grammaire n'accepte pas les appelles de fonctions n'ayant
pas de paramètre.
